﻿#include "AMachine.h"
#include "AXML.h"
#include "AString.h"

CAMachine::CAMachine() 
{
	_root = null;
	_conditionResults = null;
	_all = null;
}

CAMachine::~CAMachine()
{
	if (null != _root)
	{
		_root->release();
		_root = null;
	}
	if (null != _all)
	{
		_all->release();
		_all = null;
	}
	if (null != _conditionResults)
	{
		_conditionResults->release();
		_conditionResults = null;
	}
}

void CAMachine::addStateIntoAllStatesTable(CAState* ps)
{
	_Assert(_all);
	_all->setObject(ps, ps->getFullName());
}

void CAMachine::removeStateFromAllStatesTable(CAState* ps)
{
	_Assert(_all);
	_all->removeObjectForKey(ps->getFullName());
}

CAState* CAMachine::findState(const CAString& fullname)
{
	_Assert(_all);
	return (CAState*)_all->objectForKey(fullname);
}

CAStates* CAMachine::getAllAStates()
{
	_Assert(_all);
	return _all;
}

//tostate can be
//	* state full name		//from: leave to enter
//	* state full name@LE	//from: leave to enter
//	* state full name@lE	//from:no leave to:enter
//	* state full name@Le	//from: leave to: no enter
void CAMachine::navigateToState(ccTime time, CAState* from, const CAString& tostate)
{
	GUARD_FUNCTION();

	_Trace("FSM[%s]: %s --> %s", _file.c_str(), from->getFullName().c_str(), tostate.c_str());

	bool bEnter = true;
	bool bLeave = true;
	CAString realto = tostate;
	strings items;
	int ic = CAStringUtils::split(tostate, "@", items);
	if (ic == 1)
	{
		bEnter = bLeave = true;
	}
	else if (ic == 2)
	{
		realto = items[0];
		if (items[1] == "lE")
		{
			bEnter = true;
			bLeave = false;
		}
		else if (items[1] == "Le")
		{
			bEnter = false;
			bLeave = true;
		}
		else
		{
			if (items[1] == "LE")
			{
			}
			else
			{
				_Assert(false);
			}
			bEnter = bLeave = true;
		}
	}
	else
	{
		_Assert(false);
	}

	CAState* to = findState(realto);
	_Assert(null != to);

	_Assert(from->getDeapth() >= to->getDeapth());
	//_Assert(from != to); // 允许自跳转，重新初始化
	CAState* sleave = from;
	//leave path
	if (null == sleave->getParent() && null == to->getParent())
	{
		//machine sub state --> machine
	}
	else
	{
		while (sleave->getParent() != to->getParent())
		{
			CAState* temp = sleave->getParent();
			_Assert(temp->getCurState() == sleave);
			sleave = temp;
		}
	}
	if (bLeave)
	{
		sleave->Leave(time);
	}
	_Assert(null == sleave->getParent() || sleave->getParent()->getCurState() == sleave);
	if (null != to->getParent())
	{
		to->getParent()->setCurState(to);
	}
	if (bEnter)
	{
		to->Enter(time);
	}

	//OnEventNotify(from, SE_StateNavigated);
}

void CAMachine::setConditionResult(const char* key, bool bResult)
{
	strings items;
	_Verify(2 == CAStringUtils::split(key, "@", items));
	if (CAStringUtils::startWith(this->getLeafState()->getFullName(), items[0].c_str()))
	{
		_Trace("FSM[%s] set condition result %s = %s, in %s", _file.c_str(), key, bResult ? "true" : "false", this->getLeafState()->getFullName().c_str());
		_conditionResults->setBool(key, bResult);
	}
	else
	{
		_Warning("FSM[%s] set condition result %s = %s, in %s", _file.c_str(), key, bResult ? "true" : "false", this->getLeafState()->getFullName().c_str());
	}
}

bool CAMachine::checkCondition(CAState* from, const CATransition& trans)
{
	if (trans.condition == "true")
	{
		//now state from is a trigger point
		return true;
	}
	else if (trans.condition == "false")
	{
		return false;
	}

	CAString fullkey = from->getFullName() + "@" + trans.condition;
	const char* key = fullkey.c_str();
	if (_conditionResults->hasKey(key))
	{
		bool b = _conditionResults->getBool(key);
		_conditionResults->removeObjectForKey(key);
		return b;
	}
	return false;
}


void CAMachine::OnEventNotify(CAState* from, EStateEvent se, void* param)
{
	GUARD_FUNCTION();

	CAStateEvent* pevent = CAStateEvent::create(from, se, param);

	switch (pevent->evt)
	{
	case SE_StateNavigating:
	case SE_StateNavigated:
		break;
	case SE_StateEntering:
		break;
	case SE_StateEntered:
		onStateBegin(pevent->sender, pevent->param);
		break;
	case SE_StateLeaving:
		break;
	case SE_StateLeaved:
		onStateEnd(pevent->sender, pevent->param);
		break;
	default:
		_Assert(false);
		break;
	}
	pevent->release();
	pevent = null;
}

int CAMachine::Update(ccTime time) 
{ 
	if (null != _root)
		return _root->Update(time); 
	return -1;
};

int CAMachine::Enter(ccTime time) 
{ 
	GUARD_FUNCTION();

	if (null != _root)
		return _root->Enter(time); 
	return -1;
};

int CAMachine::Leave(ccTime time) 
{ 
	GUARD_FUNCTION();

	if (null != _root)
		return _root->Leave(time); 
	return -1;
};

CAState* CAMachine::getCurrentState()
{
	return _root->getCurState();
}

void CAMachine::initialize(CAXMLNode* pnode)
{
	GUARD_FUNCTION();

	_Assert(pnode->name() == "machine");
	_Assert(pnode->hasAttr("ref"));
	const CAString& ref = pnode->attr("ref");
	_file = ref;
	CAXML* pxml = CAXML::parse(ref.c_str(), true);
	CAXMLNode* pnodeRoot = pxml->firstnode("machine");
	_Assert(pnodeRoot);

	_Assert(null == _root);
	_Assert(null == _conditionResults);
	_Assert(null == _all);

	_root = new CAState(this);
	_root->setParent(null);
	_root->retain();
	_conditionResults = new CAValues();
	_all = new CAStates();

	_Assert(_root);
	_root->initialize(pnodeRoot);
}


